模組四|從 2D 到 3D(Day 20–25)
《矽墟》是我正在寫的一部科幻小說,拆成 200 回短篇連載,整部作品用管軟體專案的方式在管:敘事結構寫成規格檔,成品用腳本驗收。
模組三講完 2D 生圖產線。接下來六天是這整個系列的重頭戲:同一個角色,我走了兩條完全不同的 3D 路線,最後選了看起來比較笨的那條。
今天先講為什麼要做這件事,以及一個我覺得比技術選型本身更重要的動作:在看任何方案之前,先把限制寫下來。
我要替《矽墟》的其中一個角色做一頁獨立的角色誌。
如果只是展示,靜態圖就夠了,我已經有立繪和三視圖,排一排就是一頁。但我想要的不只這些:
這兩件事都需要 3D。

需求人人都會列。真正決定結果的是限制,而限制最容易在看到漂亮方案的時候被悄悄放寬。
先坦白一件事:下面這三條限制在當時是真的在運作的,但我沒有把它們寫成一份檔案。我為了寫這篇回去翻 repo,只找到 chenger-sculpt-spec.json 裡的品質契約(Day 23 講),沒有任何一份文件列著這三條。
它們存在於我腦子裡。而這篇文章接下來要講的,有一半就是這件事的後果。
三條是:
整個專案沒有 build 流程。index.html 打開就跑,Three.js 從 CDN 用 importmap 載入。
看起來像偷懶,其實是刻意的:一個個人專案最大的死因是環境爛掉。 兩年後我想改一行字,如果要先修好 node 版本、裝回一堆依賴、解決 lockfile 衝突,那我大概就不改了。
所以任何需要編譯、需要打包、需要跑轉檔工具鏈的方案,都要付出這條限制的代價。
這是一頁角色誌,不是遊戲。使用者不會為了看一個角色而等十秒。
我沒有訂一個精確的 KB 數字(後來證明這是個錯誤,Day 22 會講),但心裡的標準是「跟一般網頁差不多」。

改一個變數,模型就跟著變色,不需要重新載入任何資源。
這條限制在當下看起來很小,後來證明它是整個選型的決定性因素。
限制寫完之後,才開始看方案。
路線 A:用 image-to-3D 模型生。
我已經有三張三視圖(正面、側面、背面),這正好是 image-to-3D 模型最想要的輸入。丟進去,拿出一個帶貼圖的模型。
聽起來完美。而且 2026 年這類模型的品質已經很好了。
路線 B:把角色拆成基本幾何體,用程式碼堆出來。
球體當頭、旋轉體當和服、圓錐當裙子、環面當鐵錨的環、管狀當繩子。全部用 Three.js 內建的幾何體,一個一個擺上去。
聽起來很土。而且顯然做不到路線 A 的擬真度。
我兩條都做了,但順序跟你想的相反。
實際的時間軸是這樣(檔案與 commit 紀錄):
2026-07-27 14:08 路線 B 的規格檔
2026-07-27 14:51 路線 B 的實作
2026-07-31 14:35 路線 A 的輸入
2026-07-31 15:06 路線 A 的產出
我先做了土的那條,四天後才去試漂亮的那條。
所以別把它讀成「試了最新模型(SOTA)失敗才退回手工」的故事:我已經有一個能跑的東西了,然後才去評估要不要換掉它。而評估的標準因此完全不同:不是「這東西行不行」,是「這東西有沒有好到值得取代現有的」。
接下來的順序我照技術脈絡講而不照時間講:Day 21 講 A 怎麼跑、Day 22 講 A 的產物有什麼問題、Day 23–24 講 B 怎麼做的、Day 25 結算。
限制沒有落成檔案,也沒有寫成數字。
「載入預算」在我腦子裡是「跟一般網頁差不多」。這句話的問題在於:拿到路線 A 的產物時,我沒辦法用它判定通過還是不通過。
12 MB 算不算「跟一般網頁差不多」?如果我先寫死「模型資產不超過 2 MB」,那 12 MB 就是當場出局,我會省下後面好幾步。
但因為我寫的是模糊的形容詞,所以我做的事情變成「看看能不能壓小一點」,然後在那上面又花了時間。
模糊的限制不會擋住任何東西,它只會在事後讓你覺得「我早就覺得怪怪的」。
這跟 Day 5 講的完全是同一件事:想控制什麼,先讓它可數。 我在敘事規格上做到了(每回 20 句、一章只推進三件事、每章最多一個新名詞),在技術選型上卻沒有。
先劇透結果,因為它是後面五天的參照點:
chenger.html 865 行 35,119 bytes
整頁 34 KB。 HTML、CSS、JavaScript 全部在裡面,一個檔案,沒有其他外部資源(只有 Three.js 從 CDN 載)。
裡面有 617 行 JavaScript,全部在做同一件事:用基本幾何體把一個角色堆出來。
而路線 A 生出來的那個模型檔案是 13,206,732 bytes。
13,206,732 ÷ 35,119 = 376
那顆模型的大小,是最後上線的整頁的 376 倍。
代價一:兩條路都做,成本是雙倍。
我兩條都跑完才決定,沒有先分析就選邊。這在時間上是浪費的。
比較誠實的說法是:我當時沒有能力光靠分析判斷。 我不知道 image-to-3D 的產物實際上會有哪些問題,那些問題要拿到東西才會浮現。
如果你已經有經驗,你應該可以只走一條。這篇的價值就是讓你少走一條。
代價二:限制沒有落檔,而且太少。
沒落檔的代價是:我今天回去查,只能靠回憶重建它們。一份重建出來的限制清單,永遠有事後合理化的成分,我沒辦法排除「我現在覺得當時在乎的,其實是後來才在乎的」。
就算只算腦子裡的那三條,事後看至少還該有:
這三條在 Day 22 全部變成了問題。它們不在我的清單上,所以我在選型的當下完全沒有考慮。
代價三:「能換主題色」這條限制,我當時沒意識到它有多重。
我把它跟另外兩條並排寫,好像三條一樣重。實際上它是最後決定勝負的那一條,因為它直接否定了「貼圖烤死」的方案。(所謂烤死:顏色已經變成貼圖裡的像素,想換主題色就得整張重烤,Day 22 會實際撞到這件事。)
限制清單如果沒有分輕重,你就會在事後才發現哪一條是真正的門檻。
一、先寫限制,再看方案,而且要真的寫在檔案裡。
看到漂亮的 demo 之後才想限制,你會不自覺地替它找理由。限制要在你對任何方案產生感情之前寫下來。
「寫下來」這三個字我要特別強調,因為我自己沒做到。腦子裡的限制有兩個問題:它可以被悄悄修改而你不會發現,而且事後你無法區分「當時的限制」和「現在的合理化」。
一個 constraints.md,五行字,就解決這兩件事。
二、限制要寫成數字,形容詞擋不住任何東西。
| 寫成形容詞 | 寫成數字 |
|---|---|
| 載入要快 | 資產不超過 2 MB |
| 要好維護 | 改一個顏色不超過 5 分鐘 |
| 要能量產 | 20 個角色的總處理時間不超過 1 天 |
右邊那些可以當場判定「這個方案出局」。左邊那些只能讓你「有點猶豫」,然後繼續往下走。
三、限制清單至少要涵蓋這四類,缺一類就是一個盲區。
我只寫了第一類,然後在第二、三、四類上全部踩雷:
第 1 類是你今天看得到的,第 2–4 類是你三個月後才會痛的。而選型是一次性的決定,三個月後再痛就來不及了。
四、限制之間要排序。
寫完之後問一句:如果只能守住一條,是哪一條?
那一條就是你真正的門檻,其他的是加分項。我的答案是「能換主題色」,而我當時完全沒發現。
明天 Day 21,實際跑路線 A:Microsoft TRELLIS.2,4B 參數的 image-to-3D 模型。三張三視圖進去,一個帶完整 PBR 材質的模型出來。我會講它的核心表示法為什麼能處理衣擺和髮絲這種開放曲面,以及一個對個人創作者很現實的問題:官方要求 24GB 以上的 NVIDIA 顯卡,那沒有的人怎麼辦。